/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-26 16:27:51
 * @LastEditTime: 2021-08-04 08:31:13
 * @Description:  This files is for i2c ctrl function implementation
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 * 1.0   Zhugengyu   2021/8/3   init
 */
#include <string.h>
#include "kernel.h"
#include "parameters.h"
#include "sdkconfig.h"
#include "ft_assert.h"
#include "dw_i2c.h"
#include "dw_i2c_hw.h"
#include "fsleep.h"

/* io mux definition for ft2000-4 */
#ifdef CONFIG_TARGET_F2000_4
#ifndef CONFIG_USE_IOMUX
    #error "please enable io mux module first!!"
#endif
#include "f_iomux.h"

/* i2c io mux */
#define I2C_0_SCL_MUX       IOMUX_I2C_0_SCL_PAD
#define I2C_0_SDA_MUX       IOMUX_I2C_0_SDA_PAD
#define I2C_0_SCL_FUN       FUN0
#define I2C_0_SDA_FUN       FUN0

#define I2C_1_SCL_MUX       IOMUX_ALL_PLL_LOCK_PAD
#define I2C_1_SDA_MUX       IOMUX_ALL_PLL_LOCK_PAD
#define I2C_1_SCL_FUN       FUN2
#define I2C_1_SDA_FUN       FUN2

#define I2C_2_SCL_MUX       IOMUX_SWDO_SWJ_PAD
#define I2C_2_SDA_MUX       IOMUX_TDO_SWJ_PAD
#define I2C_2_SCL_FUN       FUN2
#define I2C_2_SDA_FUN       FUN2

#define I2C_3_SCL_MUX       IOMUX_HDT_MB_DONE_STATE_PAD
#define I2C_3_SDA_MUX       IOMUX_HDT_MB_FAIL_STATE_PAD
#define I2C_3_SCL_FUN       FUN2
#define I2C_3_SDA_FUN       FUN2

#endif

typedef struct
{
    I2cSpeed speedMode;    
    u32 sclLcnt;
    u32 sclHcnt;
    u32 sdaHold;
}I2cSpeedConfig; /* speed related configs */

typedef struct
{
	u32 speed;
	u32 minSclHightimeNs;
	u32 minSclLowtimeNs;
	u32 defRisetimeNs;
	u32 defFalltimeNs;    
}I2cSpeedModeInfo; /* speed calculation related configs */

/* info of i2c bus speed mode */
static const I2cSpeedModeInfo speedModeInfo[I2C_SPEED_MODE_MAX] = {
	[I2C_STANDARD_SPEED] = {
		I2C_SPEED_STANDARD_RATE,
		I2C_MIN_SS_SCL_HIGHTIME,
		I2C_MIN_SS_SCL_LOWTIME,
		1000,
		300,
	},
	[I2C_FAST_SPEED] = {
		I2C_SPEED_FAST_RATE,
		I2C_MIN_FS_SCL_HIGHTIME,
		I2C_MIN_FS_SCL_LOWTIME,
		300,
		300,
	},
	[I2C_HIGH_SPEED] = {
		I2C_SPEED_HIGH_RATE,
		I2C_MIN_HS_SCL_HIGHTIME,
		I2C_MIN_HS_SCL_LOWTIME,
		120,
		120,
	},
};

/**
 * @name: I2cEnable
 * @msg: enable or disable i2c ctrl
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {boolean} enable
 */
u32 I2cEnable(I2cCtrl *pCtrl, boolean enable)
{
    FT_ASSERTZERONUM(pCtrl);

    u32 status = enable ? I2C_IC_ENABLE : I2C_IC_DISABLE;
    u32 timeout = I2C_TIMEOUT;

    do
    {
        I2C_ENABLE(pCtrl, status);
        if ((I2C_ENABLE_STAT(pCtrl) & I2C_IC_ENABLE_MASK) == status)
        {
            return I2C_SUCCESS;
        }
    } while (0 != timeout--);

    I2C_INFO("timeout in %sabling I2C adapter", enable ? "en" : "dis");
    return I2C_ERR_TIMEOUT;
}

/**
 * I2cCalcCounts() - Convert a period to a number of IC clk cycles
 *
 * @icClk: Input clock in Hz
 * @periodNs: Period to represent, in ns
 * @return calculated count
 */
static uint32_t I2cCalcCounts(uint32_t busClk, uint32_t periodNs)
{
	return DIV_ROUND_UP(busClk / 1000 * periodNs, NANO_TO_KILO);
}

/**
 * @name: I2cCalcTiming
 * @msg: calculate i2c bus speed parameters
 * @return {*}
 * @param {I2cSpeedConfig} *speedCfg
 * @param {u32} icClk
 * @param {u32} spkCnt
 */
static u32 I2cCalcTiming(I2cSpeedConfig *speedCfg, u32 icClk, u32 spkCnt)
{
    FT_ASSERTZERONUM(speedCfg);
	int fallCnt, riseCnt, minTlowCnt, minThighCnt;
	int hcnt, lcnt, periodCnt, diff, tot;
	int sdaHoldTimeNs, sclRiseTimeNs, sclFallTimeNs;
    I2cSpeed mode = speedCfg->speedMode;

    const I2cSpeedModeInfo *pInfo = &speedModeInfo[mode]; 
	periodCnt = icClk / pInfo->speed;
	sclRiseTimeNs = pInfo->defRisetimeNs;
	sclFallTimeNs = pInfo->defFalltimeNs;
	riseCnt = I2cCalcCounts(icClk, sclRiseTimeNs);
	fallCnt = I2cCalcCounts(icClk, sclFallTimeNs);
	minTlowCnt = I2cCalcCounts(icClk, pInfo->minSclLowtimeNs);
	minThighCnt = I2cCalcCounts(icClk, pInfo->minSclHightimeNs);

	I2C_INFO("i2c: mode %d, icClk %d, speed %d, period %d rise %d fall %d tlow %d thigh %d spk %d\n",
	        mode, icClk, pInfo->speed, periodCnt, riseCnt, fallCnt,
	        minTlowCnt, minThighCnt, spkCnt);

	/*
	 * Back-solve for hcnt and lcnt according to the following equations:
	 * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * icClk] + SCL_Fall_time
	 * SCL_Low_time = [(LCNT + 1) * icClk] - SCL_Fall_time + SCL_Rise_time
	 */
	hcnt = minThighCnt - fallCnt - 7 - spkCnt;
	lcnt = minTlowCnt - riseCnt + fallCnt - 1;

	if (hcnt < 0 || lcnt < 0) 
    {
		I2C_ERROR("i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt);
		return I2C_ERR_INVAL_PARM;
	}

	/*
	 * Now add things back up to ensure the period is hit. If it is off,
	 * split the difference and bias to lcnt for remainder
	 */
	tot = hcnt + lcnt + 7 + spkCnt + riseCnt + 1;

	if (tot < periodCnt) 
    {
		diff = (periodCnt - tot) / 2;
		hcnt += diff;
		lcnt += diff;
		tot = hcnt + lcnt + 7 + spkCnt + riseCnt + 1;
		lcnt += periodCnt - tot;
	}

	speedCfg->sclLcnt = lcnt;
	speedCfg->sclHcnt = hcnt;

	/* Use internal default unless other value is specified */
	sdaHoldTimeNs = I2C_DEFAULT_SDA_HOLD_TIME;
	speedCfg->sdaHold = I2cCalcCounts(icClk, sdaHoldTimeNs);

	I2C_INFO("i2c: hcnt = %d lcnt = %d sda hold = %d\n", hcnt, lcnt,
	         speedCfg->sdaHold);

	return I2C_SUCCESS;                
}

/**
 * @name: I2cCalcBusSpeed
 * @msg: calculate bus speed  
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {u32} speed
 * @param {u32} busClk
 * @param {I2cSpeedConfig} *speedCfg
 */
static u32 I2cCalcBusSpeed(I2cCtrl *pCtrl, u32 speed, u32 busClk, I2cSpeedConfig *speedCfg)
{
    FT_ASSERTZERONUM(pCtrl && speedCfg);
    u32 spkCnt = 0;
    u32 ret = I2C_SUCCESS;

    if (I2C_SPEED_HIGH_RATE <= speed)
    {
        speedCfg->speedMode = I2C_HIGH_SPEED;
    }
    else if (I2C_SPEED_FAST_RATE <= speed)
    {
        speedCfg->speedMode = I2C_FAST_SPEED;
    }
    else if (I2C_SPEED_STANDARD_RATE <= speed)
    {
        speedCfg->speedMode = I2C_STANDARD_SPEED;
    }
    else
    {
        return I2C_ERR_INVAL_PARM;
    }

    spkCnt = (I2C_HIGH_SPEED == speedCfg->speedMode) ? 
             I2C_READ_HS_SPKEN(pCtrl) :
             I2C_READ_FS_SPKEN(pCtrl);

    ret = I2cCalcTiming(speedCfg, busClk, spkCnt);
    return ret;
}

/**
 * @name: I2cSetBusSpeed
 * @msg: set i2c bus speed
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {u32} speed, speed mode
 * @param {u32} busClk, bus clk
 */
static u32 I2cSetBusSpeed(I2cCtrl *pCtrl, u32 speed, u32 busClk)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 ret = I2C_SUCCESS;
    I2cSpeedConfig speedCfg;
    u32 enableStatus;
    u32 regVal;

    memset(&speedCfg, 0, sizeof(speedCfg));
    ret = I2cCalcBusSpeed(pCtrl, speed, busClk, &speedCfg);
    if (I2C_SUCCESS != ret)
        return ret;

    /* Get enable setting for restore later */
    enableStatus = I2C_ENABLE_STAT(pCtrl);

    /* to set speed cltr must be disabled */
    (void)I2cEnable(pCtrl, FALSE);

    /* clear speed mode bits */
    regVal = (I2C_READ_CON(pCtrl) & (~I2C_CON_SPEED_MASK));

    switch (speedCfg.speedMode)
    {
        case I2C_STANDARD_SPEED:
            regVal |= I2C_CON_STD_SPEED;
            I2C_WRITE_REG32(pCtrl, I2C_SS_SCL_HCNT_OFFSET, speedCfg.sclHcnt);
            I2C_WRITE_REG32(pCtrl, I2C_SS_SCL_LCNT_OFFSET, speedCfg.sclLcnt);            
            break;
        case I2C_FAST_SPEED:
            regVal |= I2C_CON_FAST_SPEED;
            I2C_WRITE_REG32(pCtrl, I2C_FS_SCL_HCNT_OFFSET, speedCfg.sclHcnt);
            I2C_WRITE_REG32(pCtrl, I2C_FS_SCL_LCNT_OFFSET, speedCfg.sclLcnt);            
            break;
        case I2C_HIGH_SPEED:
            regVal |= I2C_CON_HIGH_SPEED;
            I2C_WRITE_REG32(pCtrl, I2C_HS_SCL_HCNT_OFFSET, speedCfg.sclHcnt);
            I2C_WRITE_REG32(pCtrl, I2C_HS_SCL_LCNT_OFFSET, speedCfg.sclLcnt);
            break;    
        default:
            ret |= I2C_ERR_INVAL_PARM;
            break;
    }

    I2C_WRITE_CON(pCtrl, regVal);

    /* Configure SDA Hold Time if required */
    if (0 != speedCfg.sdaHold)
    {
        I2C_WRITE_REG32(pCtrl, I2C_SDA_HOLD_OFFSET, speedCfg.sdaHold);
    }

    /* Restore back i2c now speed set */
    if (I2C_IC_ENABLE == enableStatus)
    {
        ret |= I2cEnable(pCtrl, TRUE);
    }

    return ret;
}

/**
 * @name: I2cReset
 * @msg: reset i2c reset
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
u32 I2cReset(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);

    I2cConfig *pConfig = &pCtrl->config;
    u32 regVal = 0;
    u32 ret = I2C_SUCCESS;

    ret = I2cEnable(pCtrl, FALSE);

    if (I2C_IS_MASTER(pCtrl))
    {
        regVal |= (pConfig->use7bitAddr) ? I2C_CON_MASTER_ADR_7BIT : I2C_CON_MASTER_ADR_10BIT;
        regVal |= I2C_CON_SLAVE_DISABLE;
        regVal |= I2C_CON_MASTER_MODE;
        regVal |= I2C_CON_RESTART_EN;
        /* set as standard speed first, 
           set bus speed in following procedure */
        regVal |= I2C_CON_STD_SPEED;
    }
    else /* for slave mode */
    {
        regVal |= (pConfig->use7bitAddr) ? I2C_CON_SLAVE_ADR_7BIT : I2C_CON_SLAVE_ADR_10BIT;
        regVal |= I2C_CON_SLAVE_MODE;
        regVal |= I2C_CON_STD_SPEED;      
    }

    I2C_WRITE_CON(pCtrl, regVal);

    I2C_WRITE_RX_TL(pCtrl, I2C_RX_TL_MASK & pConfig->rxThres);
    I2C_WRITE_TX_TL(pCtrl, I2C_TX_TL_MASK & pConfig->txThres);
    I2cSetIntrruptMask(pCtrl, I2C_INTR_ALL_MASK, FALSE); /* disable all intr */

    ret |= I2cSetBusSpeed(pCtrl, pConfig->busSpeed, pConfig->refClk);
    ret |= I2cEnable(pCtrl, TRUE);

    return ret;
}

/**
 * @name: I2cSetIoMux
 * @msg: set io mux
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
static void I2cSetIoMux(I2cCtrl *pCtrl)
{
/* set io mux for ft2000-4 */
#ifdef CONFIG_TARGET_F2000_4
    int sclPadOff, sdaPadOff;
    PadFun sclFun, sdaFun;

    switch (pCtrl->config.instanceId)
    {
        case I2C_INSTANCE_0:
            sclPadOff = I2C_0_SCL_MUX;
            sdaPadOff = I2C_0_SDA_MUX;
            sclFun = I2C_0_SCL_FUN;
            sdaFun = I2C_0_SDA_FUN;
            break;
        case I2C_INSTANCE_1:
            sclPadOff = I2C_1_SCL_MUX;
            sdaPadOff = I2C_1_SDA_MUX;
            sclFun = I2C_1_SCL_FUN;
            sdaFun = I2C_1_SDA_FUN;
            break;
        case I2C_INSTANCE_2:
            sclPadOff = I2C_2_SCL_MUX;
            sdaPadOff = I2C_2_SDA_MUX;
            sclFun = I2C_2_SCL_FUN;
            sdaFun = I2C_2_SDA_FUN;
            break;
        case I2C_INSTANCE_3:
            sclPadOff = I2C_3_SCL_MUX;
            sdaPadOff = I2C_3_SDA_MUX;
            sclFun = I2C_3_SCL_FUN;
            sdaFun = I2C_3_SDA_FUN;
            break;
        default:
            FT_ASSERTVOID(0);
            break;
    }

    FIomuxSetFun(sclPadOff, sclFun);
    FIomuxSetFun(sdaPadOff, sdaFun);
#endif
}

extern u32 I2cEnableIntr(I2cCtrl *pCtrl);
/**
 * @name: I2cInit
 * @msg: init i2c ctrl in slave / master mode
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {const I2cConfig} *pConfig
 */
u32 I2cInit(I2cCtrl *pCtrl, const I2cConfig *pConfig)
{
    FT_ASSERTZERONUM(pCtrl && pConfig);
    u32 ret = I2C_SUCCESS;

    if (pConfig != &pCtrl->config)
        pCtrl->config = *pConfig;

    I2cSetIoMux(pCtrl); /* set io mux */

    ret = I2cReset(pCtrl);
    
    if (I2C_SUCCESS == ret)
    {
        pCtrl->isReady = FT_COMPONENT_IS_READY;
        pCtrl->curTrans = 0;
        pCtrl->lastErr = 0;
        pCtrl->prv = NULL;
        ret = I2cEnableIntr(pCtrl);
    }

    return ret;
}

/**
 * @name: I2cWaitStatus
 * @msg: block wait stat occur
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {u32} statBit, stat to wait
 */
static u32 I2cWaitStatus(I2cCtrl *pCtrl, u32 statBit)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 timeout = I2C_TIMEOUT;  

    /* wait until statbit was set or timeout */
    do
    {
        fsleep_millisec(2); //wait 2 ms
    } while (!(I2C_STATUS(pCtrl) & statBit) && (0 != --timeout));

    return (0 != timeout) ?I2C_SUCCESS: I2C_ERR_TIMEOUT;
}

/**
 * @name: I2cWaitBusBusy
 * @msg: block wait bus busy
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
static u32 I2cWaitBusBusy(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 timeout = I2C_TIMEOUT;
    u32 ret = I2C_SUCCESS;

    if ((I2C_STATUS(pCtrl) & I2C_STATUS_MST_ACTIVITY) && 
        (I2C_SUCCESS != I2cWaitStatus(pCtrl, I2C_STATUS_TFE)))
    {
        ret = I2C_ERR_TIMEOUT;
        I2C_ERROR("timeout in wait i2c bus busy");        
    }

    return ret;
}

/**
 * @name: I2cSetAddr
 * @msg: set slave addr for master i2c ctrl
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {u8} slaveAddr
 */
static void I2cSetAddr(I2cCtrl *pCtrl, u8 slaveAddr)
{
    u32 regVal = 0;
    (void)I2cEnable(pCtrl, FALSE);

    if (I2C_IS_MASTER(pCtrl))
    {
        regVal |= slaveAddr & I2C_IC_TAR_MASK;
        I2C_WRITE_TAR(pCtrl, regVal);
    }
    else
    {
        regVal |= slaveAddr & I2C_IC_SAR_MASK;
        I2C_WRITE_SAR(pCtrl, regVal);
    }
    
    (void)I2cEnable(pCtrl, TRUE);
}

/**
 * @name: I2cTransStart
 * @msg: start i2c read/write transaction
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {const I2cTransPack} *pRead
 */
static u32 I2cTransStart(I2cCtrl *pCtrl, const I2cTransPack *pRead)
{
    FT_ASSERTZERONUM(pCtrl && pRead);
    u32 ret = I2C_SUCCESS;
    u32 addrLen = pRead->addrLen;
    u32 regVal;

    ret = I2cWaitBusBusy(pCtrl);
    if (I2C_SUCCESS != ret)
        return ret;

    I2cSetAddr(pCtrl, pRead->slaveAddr);

    while(addrLen)
    {
        if (I2C_SUCCESS != I2cWaitStatus(pCtrl, I2C_STATUS_TFNF))
        {
            ret = I2C_ERR_TIMEOUT;
            I2C_ERROR("timeout in trans start");
            break;
        }

        if (I2C_STATUS(pCtrl) & I2C_STATUS_TFNF)
        {
            addrLen--;
            regVal = (pRead->inChipAddr >> (addrLen * BITS_PER_BYTE)) & I2C_DATA_MASK;
            I2C_WRITE_DATCMD(pCtrl, regVal);
        }
    }

    return ret;
}

/**
 * @name: I2cFlushRxFifo
 * @msg: flush rx fifo by read
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
static void I2cFlushRxFifo(I2cCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    u32 regVal;

    while (I2C_STATUS(pCtrl) & I2C_STATUS_RFNE)
    {
        regVal = I2C_READ_DATCMD(pCtrl) & I2C_DATA_MASK;
    }
}

/**
 * @name: I2cTransStop
 * @msg: stop i2c read/write transaction
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
static u32 I2cTransStop(I2cCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 regVal;
    u32 timeout = I2C_TIMEOUT;
    u32 ret = I2C_SUCCESS;

    I2C_INFO("GET MASTER STOP, stat: 0x%x, 0x%x", 
                I2C_READ_INTR_STAT(pCtrl), 
                I2C_READ_RAW_INTR_STAT(pCtrl));
    while (1)
    {
        if (I2C_READ_RAW_INTR_STAT(pCtrl) & I2C_INTR_STOP_DET)
        {
            regVal = I2C_READ_REG32(pCtrl, I2C_CLR_STOP_DET_OFFSET); /* read to clr intr status */
            break;
        }
        else if (0 == --timeout)
        {
            break;
        }
    }

    ret = I2cWaitBusBusy(pCtrl);
    if (I2C_SUCCESS == ret)
    {
        I2cFlushRxFifo(pCtrl);
    }

    return ret;
}

/**
 * @name: I2cMasterRead
 * @msg: trigger read transaction for master
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 * @param {I2cTransPack} *pRead, read buf and releated parameters
 */
u32 I2cMasterRead(I2cCtrl *pCtrl, I2cTransPack *pRead)
{
    FT_ASSERTZERONUM(pCtrl && pRead);
    u32 ret = I2C_SUCCESS;
    u32 bufLen = pRead->bufLen;
	u32 active = 0;
    int trans = (int)bufLen;
	int rxLimit, txLimit;
    u32 transTimout = 0;
    u32 regVal;

    ret = I2cTransStart(pCtrl, pRead);
    if (I2C_SUCCESS != ret)
        return ret;
        
	/*	TODO:
		触发一次发送之后去读一次fifo数据可能会无法完成，故触发与接收异步处理
		iic fifo 深度为8
		还有很多不清楚的地方，目前尝试可以连续发送了，原因不明
		主要时添加了 txLimit 判断，但是理论上没有关联
	*/
    while (bufLen)
    {
		rxLimit = 8 - I2C_READ_REG32(pCtrl, I2C_RXFLR_OFFSET);
        txLimit = 8 - I2C_READ_REG32(pCtrl, I2C_TXFLR_OFFSET);

        if (rxLimit > 0 && txLimit > 0 && trans > 0) /* trans one byte if fifo has room */
		{
			if(trans == 1)
			{
                regVal = I2C_DATA_CMD_READ | I2C_DATA_CMD_STOP ;
            }
			else
			{
                regVal = I2C_DATA_CMD_READ;
            }
            I2C_WRITE_DATCMD(pCtrl, regVal);

            trans--;
			rxLimit--;
		}
        
        /* wait for rx data */
        if (I2C_STATUS(pCtrl) & I2C_STATUS_RFNE)
        {
            /* trans one byte */
            *pRead->transBuf++ = (u8)(I2C_READ_DATCMD(pCtrl) & I2C_DATA_MASK);
            bufLen--;
            transTimout = 0;
        }
        else if (I2C_TIMEOUT < ++transTimout)
        {
            ret = I2C_ERR_TIMEOUT;
            I2C_ERROR("timeout in i2c master read");
            break;
        }     

        fsleep_millisec(2); //wait 2 ms, critical      
    }

    if (I2C_SUCCESS == ret)
    {
        ret = I2cTransStop(pCtrl);
    }

    return ret;
}

/**
 * @name: I2cMasterWrite
 * @msg: i2c 发送多字节数据
 *  * 	|slave_addr W|--|addr|--|slave_addr W|--|buffer|
 * @return {*}
 * @param {I2cCtrl} *pCtrl ,i2c ctrl blk
 * @param {I2cTransPack} *pWrite ,write ctrl blk
 */
u32 I2cMasterWrite(I2cCtrl *pCtrl, I2cTransPack *pWrite)
{
    FT_ASSERTZERONUM(pCtrl && pWrite);
    u32 ret = I2C_SUCCESS;
    u32 bufLen = pWrite->bufLen;
    u32 transTimout = 0;  
    u32 regVal;

    ret = I2cTransStart(pCtrl, pWrite);
    if (I2C_SUCCESS != ret)
        return ret;    

    while (bufLen)
    {
        if (I2C_STATUS(pCtrl) & I2C_STATUS_TFNF)
        {
            if (1 == bufLen)
            {
                regVal = (I2C_DATA_MASK & *pWrite->transBuf) | 
                          I2C_DATA_CMD_WRITE | 
                          I2C_DATA_CMD_STOP;
                I2C_INFO("Write Stop Singal");
            }
            else
            {
                regVal = (I2C_DATA_MASK & *pWrite->transBuf) |
                          I2C_DATA_CMD_WRITE;
            }
            bufLen--;

            I2C_WRITE_DATCMD(pCtrl, regVal);
            pWrite->transBuf++;
            transTimout = 0;
        }
        else if (I2C_TIMEOUT < ++transTimout)
        {
            ret = I2C_ERR_TIMEOUT;
            I2C_ERROR("timeout in i2c master write");
            break;  
        }

        fsleep_millisec(1); //wait 2 ms
    }

    if (I2C_SUCCESS == ret)
    {
        ret = I2cTransStop(pCtrl);
    }

    return ret;    
}

/**
 * @name: I2cDumpInfo
 * @msg: dump i2c ctrl setting and status
 * @return {*}
 * @param {I2cCtrl} *pCtrl
 */
void I2cDumpInfo(I2cCtrl *pCtrl)
{
    FT_ASSERTVOID(pCtrl);
    I2cConfig *pConfig = &pCtrl->config;

    I2C_INFO("=====basic info");
    I2C_INFO("work mode: %s", (I2C_MASTER == pConfig->workMode) ? "master" : "slave");
    I2C_INFO("ctrl id: %d, base addr: 0x%x", pConfig->instanceId, pConfig->baseAddr);
    I2C_INFO("=====status info");
    I2C_INFO("onoff: %s", I2C_ENABLE_STAT(pCtrl) ? "ON" : "OFF");
    I2C_INFO("ctrl reg: 0x%x", I2C_READ_CON(pCtrl));
    I2C_INFO("status reg: 0x%x", I2C_STATUS(pCtrl));
    I2C_INFO("intr mask: 0x%x", I2C_READ_INTR_MASK(pCtrl));
    I2C_INFO("intr status: 0x%x", I2C_READ_RAW_INTR_STAT(pCtrl));
}